package org.eweb4j.orm.dao;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.eweb4j.cache.DBInfoConfigBeanCache;
import org.eweb4j.config.LogFactory;
import org.eweb4j.orm.config.ORMConfigBeanUtil;
import org.eweb4j.orm.dao.config.DAOConfigConstant;
import org.eweb4j.orm.jdbc.DataSourceWrapCache;
import org.eweb4j.orm.jdbc.JdbcUtil;
import org.eweb4j.util.StringUtil;

public class DAOImpl implements DAO {
	final private Class<?> clazz;
	final private String dsName;
	private StringBuilder condition = new StringBuilder("");
	private List<Object> args;
	private String dbType;
	private DataSource ds;
	private String table;

	public DAOImpl(Class<?> clazz, String dsName) {
		if (dsName == null)
			dsName = DAOConfigConstant.MYDBINFO;
		this.clazz = clazz;
		this.dsName = dsName;
		this.ds = DataSourceWrapCache.get(dsName);
		args = new ArrayList<Object>();
		dbType = DBInfoConfigBeanCache.get(dsName).getDataBaseType();
		if (Map.class.isAssignableFrom(clazz)) {
		} else {
			this.table = ORMConfigBeanUtil.getTable(clazz);
		}
	}

	public DAOImpl append(String str) {
		String column = ORMConfigBeanUtil.getColumn(clazz, str);
		if (column == null)
			column = str;

		this.condition.append(" ").append(str).append(" ");
		return this;
	}

	public DAOImpl field(String fieldName) {
		String column = ORMConfigBeanUtil.getColumn(clazz, fieldName);
		this.condition.append(" ").append(column).append(" ");
		return this;
	}

	public DAOImpl notLike(Object value) {
		this.condition.append(" NOT LIKE '").append(value).append("' ");
		return this;
	}

	public DAOImpl notEqual(Object value) {
		this.condition.append(" <> '").append(value).append("' ");
		return this;
	}

	public DAOImpl equal(Object value) {
		this.condition.append(" = '").append(value).append("' ");
		return this;
	}

	public DAOImpl moreThan(Object value) {
		this.condition.append(" > '").append(value).append("' ");
		return this;
	}

	public DAOImpl lessThan(Object value) {
		this.condition.append(" < '").append(value).append("' ");
		return this;
	}

	public DAOImpl or(String fieldName) {
		String column = ORMConfigBeanUtil.getColumn(clazz, fieldName);
		this.condition.append(" OR ").append(column).append(" ");
		return this;
	}

	public DAOImpl and(String fieldName) {
		String column = ORMConfigBeanUtil.getColumn(clazz, fieldName);
		this.condition.append(" AND ").append(column).append(" ");
		return this;
	}

	public DAOImpl desc(String fieldName) {
		String column = ORMConfigBeanUtil.getColumn(clazz, fieldName);
		this.condition.append(" ORDER BY ").append(column).append(" DESC ");
		return this;
	}

	public DAOImpl asc(String fieldName) {
		String column = ORMConfigBeanUtil.getColumn(clazz, fieldName);
		this.condition.append(" ORDER BY ").append(column).append(" ASC ");
		return this;
	}

	// --------------------------------------------------------

	public <T> List<T> query() {
		String sql = condition.toString().replace("'?'", "?");
		List<T> result = null;
		try {

			if (Map.class.isAssignableFrom(this.clazz)) {
				Connection con = ds.getConnection();
				if (args != null && args.size() > 0) {
					result = (List<T>) JdbcUtil.getListWithArgs(con, clazz,
							sql, args.toArray(new Object[] {}));
				} else {
					result = (List<T>) JdbcUtil.getList(con, clazz, sql);
				}

			} else {
				if (args != null && args.size() > 0) {
					result = (List<T>) DAOFactory.getSelectDAO(dsName)
							.selectBySQL(clazz, sql,
									args.toArray(new Object[] {}));
				} else {
					result = (List<T>) DAOFactory.getSelectDAO(dsName)
							.selectBySQL(clazz, condition.toString());
				}

			}

			this.clear();
			return result;
		} catch (Exception e) {
			LogFactory.getORMLogger("error").write(
					"sql-->" + sql + "exception:"
							+ StringUtil.getExceptionString(e));
			throw new DAOException(sql + " execute exception", e);
		}
	}

	public <T> T queryOne() {
		List<T> list = query();
		T result = list == null ? null : list.size() > 0 ? list.get(0) : null;
		return result;
	}

	public DAO selectStr(String str) {
		if (str == null || clazz == null)
			return this;

		this.condition.append(" SELECT ").append(str).append(" FROM ")
				.append(table).append(" ");

		return this;
	}

	public DAO insert(String[] fields) {
		if (clazz == null)
			return this;

		StringBuilder sb = new StringBuilder();
		String[] columns;
		if (fields == null)
			columns = ORMConfigBeanUtil.getColumns(clazz);
		else
			columns = ORMConfigBeanUtil.getColumns(clazz, fields);

		for (String col : columns) {
			if (sb.length() > 0)
				sb.append(", ");
			sb.append(col);
		}

		this.condition.append(" INSERT INTO ").append(table).append("(")
				.append(sb.toString()).append(") ");

		return this;
	}

	public DAO values(Object[] values) {
		if (values == null) {
			values = new Object[ORMConfigBeanUtil.getColumns(clazz).length];

			for (int i = 0; i < values.length; i++)
				values[i] = "?";
		}

		StringBuilder sb = new StringBuilder();
		for (Object val : values) {
			if (sb.length() > 0)
				sb.append(", ");
			sb.append("'").append(val).append("'");
		}
		this.condition.append(" VALUES(").append(sb.toString()).append(") ");

		return this;
	}

	public DAO where() {
		this.condition.append(" WHERE ");

		return this;
	}

	public int execute() {
		int id = -1;
		String sql = this.condition.toString().replace("'?'", "?");
		DataSource ds = DataSourceWrapCache.get(dsName);
		try {
			int rs = 0;
			if (args != null && args.size() > 0) {
				rs = (Integer) JdbcUtil.updateWithArgs(ds.getConnection(), sql,
						args.toArray(new Object[] {}));
			} else {
				rs = (Integer) JdbcUtil.update(ds.getConnection(), sql);
			}

			if (rs > 0 && sql.contains("INSERT INTO"))
				id = (Integer) DAOUtil.selectMaxId(clazz, ds.getConnection(),
						dbType);

		} catch (SQLException e) {
			LogFactory.getORMLogger("error").write(
					"sql-->" + sql + "exception:"
							+ StringUtil.getExceptionString(e));
			throw new DAOException(sql + " execute exception", e);
		}

		this.clear();
		return id;
	}

	public DAO update() {
		if (clazz == null)
			return this;

		this.condition.append(" UPDATE ").append(table).append(" ");

		return this;
	}

	public DAO set(String[] fields, Object[] values) {
		if (fields == null || values == null || fields.length == 0
				|| values.length == 0 || fields.length != values.length)
			return this;

		StringBuilder sb = new StringBuilder();
		String[] columns = ORMConfigBeanUtil.getColumns(clazz, fields);
		for (int i = 0; i < values.length; i++) {
			if (sb.length() > 0)
				sb.append(", ");
			String col = columns[i];
			Object val = values[i];
			sb.append(col).append(" = '").append(val).append("'");
		}

		this.condition.append(" SET ").append(sb.toString()).append(" ");

		return this;
	}

	public DAO delete() {
		if (clazz == null)
			return this;

		this.condition.append(" DELETE FROM ").append(table).append(" ");

		return this;
	}

	public DAO selectAll() {
		this.condition.append(" SELECT * FROM ").append(table).append(" ");
		return this;
	}

	public DAO select(String[] fields) {
		if (fields == null || clazz == null)
			return this;

		StringBuilder sb = new StringBuilder();
		for (String field : fields) {
			if (sb.length() > 0)
				sb.append(", ");
			String col = ORMConfigBeanUtil.getColumn(clazz, field);
			sb.append(col);
		}
		this.condition.append(" SELECT ").append(sb.toString())
				.append(" FROM ").append(table).append(" ");

		return this;
	}

	public DAO likeLeft(Object value) {
		this.condition.append(" LIKE '").append(value).append("%' ");
		return this;
	}

	public DAO likeRight(Object value) {
		this.condition.append(" LIKE '%").append(value).append("' ");
		return this;
	}

	public DAO like(Object value) {
		this.condition.append(" LIKE '%").append(value).append("%' ");
		return this;
	}

	public DAO clear() {
		this.condition = null;
		this.condition = new StringBuilder();
		this.args = null;
		this.args = new ArrayList<Object>();

		return this;
	}

	public DAO in(Object[] values) {
		if (values == null)
			return this;

		this.condition.append(" in(");

		StringBuilder sb = new StringBuilder();
		for (Object o : values) {
			if (sb.length() > 0)
				sb.append(", ");

			sb.append("'").append(o).append("'");
		}

		this.condition.append(sb.toString());

		this.condition.append(") ");

		return this;
	}

	public DAO notIn(Object[] values) {
		if (values == null)
			return this;

		this.condition.append(" not in(");

		StringBuilder sb = new StringBuilder();
		for (Object o : values) {
			if (sb.length() > 0)
				sb.append(", ");

			sb.append("'").append(o).append("'");
		}

		this.condition.append(sb.toString());

		this.condition.append(") ");

		return this;
	}

	public DAO inSql(String sql) {
		if (sql == null)
			return this;

		this.condition.append(" in(").append(sql).append(") ");

		return this;
	}

	public DAO notInSql(String sql) {
		if (sql == null)
			return this;

		this.condition.append(" not in(").append(sql).append(") ");

		return this;
	}

	public String toSql() {
		return this.condition.toString().replace("'?'", "?");
	}

	public DAO fillArgs(Object[] args) {
		if (args != null) {
			for (Object arg : args)
				this.args.add(arg);
		}

		return this;
	}

	public DAO setTable(String table) {
		this.table = table;
		return this;
	}

}
